import time,threading,pdb
import numpy as np
ar=np.array

class PhysicsInfo:
    def __init__(self,view,v=0,ug=0.2):
        #m=1
        self.view=view
        self.v=v
        self.ug=ug
    def changev(self,dv):
        self.v=self.v+dv
    def go(self):
        c=np.dot(self.v,self.v)**0.5
        fa=ar([self.ug*(self.v[0]/c),self.ug*(self.v[1]/c)])
        self.v=self.v-fa
        self.view.pos=self.view.pos+self.v


class Animation:
    import ctypes,os
    ctypes=ctypes
    def __init__(self,view,sleep):
        self.view=view
        self.sleep=sleep
        self.alive=False
    def play(self,*args):
        self.playThread=threading.Thread(target=self._play_controller,daemon=True,args=args)
        self.playThread.start()
    def _play_controller(self,*args):
        try:    self.view.animations[type(self)].stop()
        except: pass
        self.view.animations[type(self)]=self
        self.alive=True
        self.animation()
        self.alive=False
        del self.view.animations[type(self)]
    def stop(self):
        self.alive=False
        self.playThread.join()
    def animation(self):
        pass


class Spring:
    def __init__(self,k=0.03,pos=ar([0,0])):
        self.k=k
        self.pos=pos
    def force(self,pos):
        #原长就是0
        return -self.k*(pos-self.pos)
    def work(self,x0,x1):
        return self.k*x0**2/2-self.k*x1**2/2
class Bounce(Animation):
    def __init__(self,view,destpos,spring=None,phyinfo=None,sleep=0.01,finishCallBack=lambda : None):
        super().__init__(view,sleep)
        self.destpos=destpos
        if spring==None:    self.spring=Spring(pos=destpos)
        else:               self.spring=spring
        if phyinfo==None:   self.phyinfo=PhysicsInfo(self.view,v=ar([0,-30]))
        else:               self.phyinfo=phyinfo
        self.finishCallBack=finishCallBack
        self.sleep=sleep
    def animation(self):
        #过程1：拉住到停
        v0=np.copy(self.phyinfo.v)#一开始的速度，用于判断是否停下
        while np.dot(self.phyinfo.v,v0)>0 and self.alive:#没停下之前一直走
            time.sleep(self.sleep)
            self.phyinfo.changev(self.spring.force(self.view.pos))
            self.phyinfo.go()
        if not self.alive: return
        self.phyinfo.v=0
        #弹簧达到最长

        #过程2：拉回来，直到第二个弹簧恢复原长
        d_array=self.destpos-self.view.pos#离目标点还有多远？（这里是向量）
        d=np.dot(d_array,d_array)**0.5#求出距离
        E_more=self.phyinfo.ug*d#-np.dot(self.phyinfo.v,self.phyinfo.v)/2 #m=1   求出（新的）弹簧应该提供的能量

        k=0.03
        x=(2*E_more/k)**0.5#提供的能量=弹性势能，由此求得弹簧长
        spring2=Spring(
            k=k,
            pos=self.view.pos+ar([x*d_array[0]/d,x*d_array[1]/d])
            )#创建新的弹簧，利用三角函数摆放到正确位置
        d1=self.view.pos-spring2.pos#记录弹簧到物体的向量，让弹簧拉着物体走，直到弹簧恢复原长，此时删除弹簧，让物体匀减速达到目标点
        while np.dot(d1,self.view.pos-spring2.pos)>0 and self.alive:#没有达到原长就一直让弹簧拉着
            time.sleep(self.sleep)
            self.phyinfo.changev(spring2.force(self.view.pos))
            self.phyinfo.go()
        if not self.alive: return
        #弹簧恢复原长（原长就是0）   

        #过程3：过程2结束直到物体由于摩擦力作用停下
        #记录现在的速度，用来检测速度变为0或反向
        v3=np.copy(self.phyinfo.v)
        while np.dot(self.phyinfo.v,v3)>0 and self.alive:
            time.sleep(self.sleep)
            self.phyinfo.go()
        if not self.alive: return
        #按理说是停下了，但是每次摩擦力都会直接把速度加上去，有可能速度变成负的。此时应该强制设置为0，并且把位置也强制设置为目标位置。
        self.phyinfo.v=0
        self.view.pos=self.destpos
        self.finishCallBack()